﻿using System;
using System.Net.NetworkInformation;
using System.IO;

namespace Game.Lib.ztPackets
{
    public class LoginPacket : Packet
    {
        /// <summary>
        /// The salt data used for the password hash
        /// </summary>
        public const string SALT_DATA = "S@|_7eye|\\|g<3Oh*$";

        /// <summary>
        /// The login user name
        /// </summary>
        public string Username;

        /// <summary>
        /// The login password
        /// </summary>
        public string Password;

        /// <summary>
        /// The region name to log into, "" to login to the last location, or "home" to login to the agent's home
        /// </summary>
        public string Region;

        /// <summary>
        /// The version name for this client
        /// </summary>
        public string Version;

        /// <summary>
        /// he byte array representing the client's MAC adderess
        /// </summary>
        public byte[] MAC;

        /// <summary>
        /// Unique ID for this login attempt
        /// </summary>
        public UUID LoginID;

        public byte[] PasswordHash
        {
            get
            {
                return _PassHash;
            }
        }

        private byte[] _PassHash;

        public LoginPacket()
        {
            ID = Utils.GetPacketID();
            Type = PacketType.Login;
            Username = "";
            Password = "";
            Region = "";
            Version = "Default Client 1.0.0";
            MAC = GetMAC();
            LoginID = new UUID();
            RecalcHash();
        }

        public LoginPacket(string name, string pass) : this()
        {
            Username = name;
            Password = pass;
            RecalcHash();
        }

        public LoginPacket(string name, string pass, string region, string version) : this(name, pass)
        {
            Region = region;
            Version = version;
            MAC = GetMAC();
            LoginID = new UUID();
            RecalcHash();
        }

        public override int Length
        {
            get
            {
                return 4 + Utils.UTF8.GetByteCount(Username) + 32 + 4 + Utils.UTF8.GetByteCount(Region) + 4 + Utils.UTF8.GetByteCount(Version) + 6 + 16;
            }
        }

        public override byte[]  ToBytes()
        {
            MemoryStream ms = new MemoryStream();

            ms.Write(Utils.GetBytes(Username), 0, Utils.UTF8.GetByteCount(Username) + 4);
            ms.Write(PasswordHash, 0, 32);
            ms.Write(Utils.GetBytes(Region), 0, Utils.UTF8.GetByteCount(Region) + 4);
            ms.Write(Utils.GetBytes(Version), 0, Utils.UTF8.GetByteCount(Version) + 4);
            ms.Write(MAC, 0, 6);
            LoginID.Serialize(ms);

            return GetTotalBuffer(ms.ToArray());
        }

        public override void FromBytes(byte[] data, int pos)
        {
            MemoryStream ms = new MemoryStream(data);
            ms.Position = pos;
            Username = Utils.GetString(ms);
            if (ms.Read(_PassHash, 0, 32) != 32) throw new EndOfStreamException();
            Region = Utils.GetString(ms);
            Version = Utils.GetString(ms);
            if (ms.Read(MAC, 0, 6) != 6) throw new EndOfStreamException();
            LoginID.Deserialize(ms);
        }

        public override bool Equals(object obj)
        {
            if (!(obj is LoginPacket)) return false;
            LoginPacket lp = (LoginPacket)obj;
            if(lp.Hash != this.Hash ||
                lp.ID != this.ID ||
                lp.LoginID != this.LoginID ||
                lp.Region != this.Region ||
                lp.Username != this.Username ||
                lp.Version != this.Version) return false;
            for (int i = 0; i < 6; i++) if (lp.MAC[i] != this.MAC[i]) return false;
            for (int i = 0; i < 32; i++) if (lp.PasswordHash[i] != this.PasswordHash[i]) return false;
            return true;
        }

        public override int GetHashCode()
        {
            return ID.GetHashCode() ^ LoginID.GetHashCode() ^ Region.GetHashCode() ^ Username.GetHashCode() ^ Version.GetHashCode() ^ MAC.GetHashCode() ^ PasswordHash.GetHashCode();
        }

        private byte[] GetMAC()
        {
            NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
            string mac = "";
            if (nics != null && nics.Length > 0)
            {
                foreach (NetworkInterface ni in nics)
                {
                    string adaperMac = ni.GetPhysicalAddress().ToString().ToUpper();
                    if (adaperMac.Length == 12 && adaperMac != "000000000000")
                    {
                        mac = adaperMac;
                    }
                }
            }
            if (mac.Length < 12) mac = new UUID().ToString().Substring(24, 12);
            return new byte[] {
                Byte.Parse(mac.Substring(0, 2), System.Globalization.NumberStyles.HexNumber),
                Byte.Parse(mac.Substring(2, 2), System.Globalization.NumberStyles.HexNumber),
                Byte.Parse(mac.Substring(4, 2), System.Globalization.NumberStyles.HexNumber),
                Byte.Parse(mac.Substring(6, 2), System.Globalization.NumberStyles.HexNumber),
                Byte.Parse(mac.Substring(8, 2), System.Globalization.NumberStyles.HexNumber),
                Byte.Parse(mac.Substring(10, 2), System.Globalization.NumberStyles.HexNumber) };
        }

        private void RecalcHash()
        {
            _PassHash = Utils.CSP.ComputeHash(Utils.UTF8.GetBytes(Username + SALT_DATA + Password + Region + SALT_DATA + Version + MAC + SALT_DATA + LoginID.ToString()));
        }
    }
}
